import numpy as np
import taichi as ti
# pylint: disable=missing-function-docstring,trailing-whitespace,unused-argument
# pylint: disable=consider-using-f-string,no-value-for-parameter

from .misc import (load_font)

vec4 = ti.math.vec4
vec3 = ti.math.vec3

class Rect:
    """
    矩形
    """
    def __init__(self,x=0,y=0,w=0,h=0):
        self.rc = np.array([x,y,x+w,y+h])

    def get_xy(self):
        return self.rc[0],self.rc[1]
    
    def get_size(self):
        return self.rc[2]-self.rc[0],self.rc[3]-self.rc[1]
    
    def in_rect(self,x,y):
        return (x>self.rc[0] and x<self.rc[2] and
                y>self.rc[1] and y<self.rc[3])
    
@ti.data_oriented
class UiWindow:
    """
    窗口
    """
    def __init__(self,x,y,w,h):
        self.pos = (int(x),int(y))
        self.size = (int(w),int(h))
        self.rect = ti.Vector([int(x),int(y),int(x+w),int(y+h)])
        self.pixels = ti.Vector.field(4, float, self.size) # 图像的像素场
        font,rc = load_font()
        self.font = font
        self.font_rc = rc
        self.ui = []
        self.fill_rect2(0,0,w,h,ti.Vector([0.,0.,0.,0.2]))
        self.capture_ui = {}
        self.after_del = []
        #self.draw_text("3.141592608753210",0,0,ti.Vector([0.,0.,0.,1.]))
        #self.draw_text("Hello World 3.14% $.",0,16,ti.Vector([0.,0.,0.,1.]))

    def add_capture(self,ui):
        self.capture_ui[ui] = True

    def del_capture(self,ui):
        self.after_del.append(ui)

    def on_mouse(self,b,x,y):
        """
        鼠标事件b = 0 没有按下 1左键按下 2 右键按下
        x,y 光标位置
        """
        for k in self.capture_ui:
            k.on_mouse(b,x,y)
        if len(self.after_del)>0:
            for del_ui in self.after_del:
                del self.capture_ui[del_ui]
            self.after_del.clear()

        for item in self.ui:
            if item.on_mouse(b,x,y):
                return True
        return False

    def fetch_image(self):
        return self.pixels

    @ti.kernel
    def fill_rect2(self,x:int,y:int,w:int,h:int,c:vec4):# pylint: disable=no-value-for-parameter
        """
        填充矩形区域
        """
        shape = self.pixels.shape
        if not (y>=shape[1] or x>=shape[0] or y+h<=0 or x+w<=0):
            x0 = ti.max(0,x)
            y0 = ti.max(0,y)        
            x1 = ti.min(shape[0],x+w)
            y1 = ti.min(shape[1],y+h)
            for j in range(y0,y1):
                for i in range(x0,x1):
                    self.pixels[i,self.size[1]-j-1] = c

    def fill_rect(self,rect:Rect,c:vec4):
        rc = rect.rc
        self.fill_rect2(int(rc[0]),int(rc[1]),int(rc[2]-rc[0]),int(rc[3]-rc[1]),c)

    @ti.kernel
    def draw_text2(self,a:ti.types.ndarray(),x:int,y:int,c:vec4):
        x_i = x
        ti.loop_config(serialize=True)
        for i in range(a.shape[0]):
            rc = self.font_rc[a[i]]
            if (x_i+rc[2]-rc[0]<self.pixels.shape[0] and 
                y+rc[3]-rc[1]<self.pixels.shape[1]):
                for yy in range(rc[1],rc[3]):
                    yy_ = rc[3] + rc[1] - yy - 1
                    for xx in range(rc[0],rc[2]):
                        alpha = self.font[xx,yy_].a
                        y_ = self.size[1] - (y+yy-rc[1])
                        self.pixels[x_i+xx-rc[0],y_] = (alpha * c +
                            self.pixels[x_i+xx-rc[0],y_] *(1-alpha))
            
            x_i += (rc[2]-rc[0])

    def draw_text(self,label:str,x:int,y:int,c:vec4):
        """
        在rc中绘制文本label
        """
        self.draw_text2(np.array(list(label.encode(encoding='ascii'))),x,y,c)
        
    def update(self):
        """
        更新 pixels 重新绘制所有元件
        """
        for item in self.ui:
            item.relayout()
        for  item in self.ui:
            item.draw(self)

    def add_ui(self,ui,x,y,w=None,h=None):
        ui.set_rect(Rect(x,y,w if w else self.size[0],h if h else self.size[1]))
        self.ui.append(ui)

class UiItem:
    """
    界面原件
    """
    def __init__(self):
        self.rect = Rect()

    def on_mouse(self,b,x,y):
        """
        鼠标事件b = 0 没有按下 1左键按下 2 右键按下
        x,y 光标位置        
        """
        return False

    def draw(self,window:UiWindow):
        """
        绘制界面元件
        """
        return
    
    def get_size(self)->Rect:
        return 0,0
    
    def relayout(self):
        return
    
    def set_rect(self,rc:Rect):
        self.rect = rc

class UiSlider(UiItem):
    """
    调节滑块
    """
    def __init__(self,cb,minv=0.,maxv=1.,value=0.,
                 color=(1.,0.,0.,0.0),slider_color=(0.,1.,1.,1.),index=0):
        super().__init__()
        self.cb = cb
        self.min = minv
        self.max = maxv
        self.value = value
        self.color = color
        self.slider_color = slider_color
        self.cur_color = slider_color
        self.win = None
        self.b = False
        self.is_capture = False
        self.index = index
        self.old_value = self.value

    def on_mouse(self,b,x,y):
        if self.rect.in_rect(x,y):
            xx,_ = self.rect.get_xy()
            w,_ = self.rect.get_size()
            value_x = w * (self.value-self.min)/(self.max-self.min) + xx
            isf = x>value_x-4 and x<value_x+4
            if isf:
                self.cur_color = (1.,1.,0.,1.)
                self.win.add_capture(self)
                self.old_value = self.value
                self.is_capture = True
            else:
                self.cur_color = self.slider_color
            if b==1 and isf and not self.b:
                self.b = True
            elif b==1:
                self.value = min(self.max,max(self.min,(self.max-self.min)*(x-xx)/w+self.min))
            elif b==1 and self.b:
                self.value = min(self.max,max(self.min,(self.max-self.min)*(x-xx)/w+self.min))
            elif b==0 and self.b:
                self.b = False            
                self.on_change(self.value)
            
            self.draw(self.win)
            return True
        else:
            self.b = False
            if self.is_capture:
                self.is_capture = False
                self.win.del_capture(self)
                self.cur_color = self.slider_color
                if self.old_value !=self.value:
                    self.on_change(self.value)
                self.draw(self.win)
        return False
    
    def on_change(self,value):
        if self.cb:
            self.cb(value,self.index)

    def draw(self,window:UiWindow):
        self.win = window
        x,y = self.rect.get_xy()
        w,h = self.rect.get_size()
        window.fill_rect(self.rect,ti.Vector(self.color))
        xx = (w-4) * (self.value-self.min)/(self.max-self.min)
        window.draw_text("%.2f"%(self.value),x,y,ti.Vector([0.2,0.2,0.2,.9]))
        window.fill_rect(Rect(x+xx,y,4,h),ti.Vector(self.cur_color))

    def get_size(self):
        return 0,20

class UiLabel(UiItem):
    """
    文本标签
    """
    def __init__(self,label:str,color=(0.,0.,0.,1.)):
        super().__init__()
        self.label = label
        self.color = color

    def draw(self,window:UiWindow):
        window.draw_text(self.label,self.rect.rc[0],self.rect.rc[1],ti.Vector(self.color))
        
    def get_size(self):
        return 0,16
    
class UiButton(UiItem):
    """
    Button
    """
    def __init__(self,txt:str,cb=None,color=(0.2,0.2,0.8,0.5),label_color=(0.,0.,0.,1.)):
        super().__init__()
        self.label = txt
        self.cb = cb
        self.b = 0
        self.color = color
        self.label_color = label_color
        self.win = None

    def on_click(self):
        if self.cb:
            self.cb()

    def on_mouse(self, b, x, y):
        if self.rect.in_rect(x,y):
            if b==1 and self.b==0:
                self.b = 1
            elif b==0 and self.b==1:
                self.b = 0
                self.on_click()
            return True
        elif self.b!=0:
            self.b = 0
        return False

    def draw(self,window:UiWindow):
        self.win = window
        window.fill_rect(self.rect,ti.Vector(self.color))
        window.draw_text(self.label,self.rect.rc[0],self.rect.rc[1],ti.Vector(self.label_color))

    def get_size(self):
        return 0,20

class UiCheck(UiItem):
    """
    CheckBox
    """
    def __init__(self,cb=None,check=False,
                 color=(0.2,0.2,0.8,0.5),index=0):
        super().__init__()
        self.cb = cb
        self.b = 0
        self.index = index
        self.check = check
        self.color = color
        self.win = None

    def on_click(self):
        if self.cb:
            self.check = not self.check
            self.cb(self.check,self.index)
            self.draw(self.win)

    def on_mouse(self, b, x, y):
        if self.rect.in_rect(x,y):
            if b==1 and self.b==0:
                self.b = 1
            elif b==0 and self.b==1:
                self.b = 0
                self.on_click()
            return True
        elif self.b!=0:
            self.b = 0
        return False
            
    def draw(self,window:UiWindow):
        self.win = window
        code = b"\0"
        if self.check:
            code = b"\1"
        window.fill_rect(self.rect,ti.Vector((0.8,0.8,0.8,0.2)))
        window.draw_text(code.decode(encoding="ascii"),
                         self.rect.rc[0],
                         self.rect.rc[1],
                         ti.Vector(self.color))

    def get_size(self):
        return 0,20
    
class UiTreeItem(UiItem):
    """
    树形界面
    """
    def __init__(self,txt = None,isopen=False,
                 color=(0.2,0.2,0.2,0.5),label_color=(.8,0.8,.8,1.)):
        super().__init__()
        self.items = []
        self.isopen = isopen
        self.color = color
        self.win = None
        def click():
            self.isopen = not self.isopen
            self.win.update()
        self.title = UiButton(txt,click,color=color,label_color=label_color) if txt else None
        
    def relayout(self):
        """
        重新排列窗口位置
        """
        x,y = self.rect.get_xy()
        w,_ = self.rect.get_size()
        dx = 0
        dy = 128
        if self.title:
            self.title.set_rect(Rect(x,y,w,20))
            y += 20
            dx = 16
        if self.isopen:
            for item in self.items:
                if isinstance(item,tuple):
                    item[0].set_rect(Rect(x+dx,y,w-dx,20))
                    item[1].set_rect(Rect(x+dx+dy,y,w-dx-dy,20))
                    y += 20
                else:
                    _,hh = item.get_size()
                    item.set_rect(Rect(x+dx,y,w-dx,hh))
                    item.relayout()
                    y += hh

    def add_properties(self,name,cb=None,minv=0.,maxv=1.,
                       value=0.,index=0,label_color=(0.,0.,0.,1.),
                       slider_bg=(1.,0.,0.,0.0),slider_color=(0.,1.,1.,1.)):
        self.items.append((UiLabel(name,color=label_color),
                           UiSlider(cb,minv,maxv,value=value,index=index,
                                    color=slider_bg,slider_color=slider_color)))
        
    def add_check(self,name,cb=None,check=False,index=0,
                  color=(0.,0.,0.,1.),label_color=(0.,0.,0.,1.)):
        self.items.append((UiLabel(name,color=label_color),
                           UiCheck(cb,check,color=color,index=index)))

    def add_child(self,child):
        """
        加入一个子元件
        """
        self.items.append(child)

    def clear(self):
        self.items.clear()

    def draw(self,window:UiWindow):
        self.win = window
        x,y = self.rect.get_xy()
        w,h = self.rect.get_size()        
        self.win.fill_rect2(x,y,w,h,ti.Vector(self.color))
        if self.title:
            self.title.draw(window)
        if self.isopen:
            for item in self.items:
                if isinstance(item,tuple):
                    item[0].draw(window)
                    item[1].draw(window)
                else:
                    item.draw(window)

    def on_mouse(self, b, x, y):
        bb = False
        if self.title:
            bb = self.title.on_mouse(b,x,y)
        if not bb and self.isopen:
            for item in self.items:
                if isinstance(item,tuple):
                    bb = item[1].on_mouse(b,x,y)
                else:
                    bb = item.on_mouse(b,x,y)
                if bb:
                    break
        return bb

    def get_size(self):
        """
        重新计算元件尺寸
        """
        w,h = self.title.get_size() if self.title else (0,0)
        if self.isopen:
            for item in self.items:
                if isinstance(item,tuple):
                    x0,y0 = item[0].get_size()
                    x1,y1 = item[1].get_size()
                    x = min(x0,x1)
                    y = max(y0,y1)
                else:
                    x,y = item.get_size()
                w += x
                h += y
        return w,h
